package main

import (
	"fmt"
	"sync"
	"time"
)

type Item struct {
	value string
}

type Queue struct {
	items        []Item
	lock         sync.RWMutex
	condNotEmpty *sync.Cond
	condNotFull  *sync.Cond
	maxSize      int
}

func NewQueue(maxSize int) *Queue {
	q := &Queue{maxSize: maxSize}
	q.condNotEmpty = sync.NewCond(&q.lock)
	q.condNotFull = sync.NewCond(&q.lock)
	return q
}

func (q *Queue) Enqueue(item Item) {
	q.lock.Lock()
	for len(q.items) == q.maxSize {
		q.condNotFull.Wait()
	}
	q.items = append(q.items, item)
	q.condNotEmpty.Signal()
	q.lock.Unlock()
}

func (q *Queue) Dequeue() *Item {
	q.condNotEmpty.L.Lock()
	for len(q.items) == 0 {
		q.condNotEmpty.Wait()
	}
	item := q.items[0]
	q.items = q.items[1:len(q.items)]
	q.condNotFull.Signal()
	q.condNotEmpty.L.Unlock()
	return &item
}

func producer(q *Queue, id int) {
	for {
		time.Sleep(time.Second)
		item := Item{value: fmt.Sprintf("producer %d", id)}
		q.Enqueue(item)
		fmt.Printf("Producer %d produced an item\n", id)
	}
}

func consumer(q *Queue, id int) {
	for {
		item := q.Dequeue()
		fmt.Printf("Consumer %d consumed an item: %s\n", id, item.value)
	}
}

//func main() {
//	q := NewQueue(100)
//	for i := 0; i < 5; i++ {
//		go producer(q, i)
//	}
//	for i := 0; i < 5; i++ {
//		go consumer(q, i)
//	}
//	select {}
//}
